<?php

class DaBase_Router {
	
	protected $tablesPrefix;
	protected $gettersClassesPrefix;
	protected $objectsClassesPrefix;
	protected $defaultGettersClass = 'DaBase_Getter';
	protected $defaultObjectsClass = 'DaBase_Object';
	protected $throwExceptionOnWrongSubclass = true;

	public function __construct($gettersClassesPrefix = null, $objectsClassesPrefix = null, $defaultGettersClass = null, $defaultObjectsClass = null, $tablesPrefix = null) {
		if ($defaultGettersClass) {
			$this->defaultGettersClass = $defaultGettersClass;
		}
		if (!$this->isClassExists($this->defaultGettersClass, 'DaBase_Getter')) {
			throw new DaBase_Exception('Default getters class "' . $this->defaultGettersClass . '" must be subclass of "DaBase_Getter"');
		}
		if ($defaultObjectsClass) {
			$this->defaultObjectsClass = $defaultObjectsClass;
		}
		if (!$this->isClassExists($this->defaultObjectsClass, 'DaBase_Object')) {
			throw new DaBase_Exception('Default objects class "' . $this->defaultObjectsClass . '" must be subclass of "DaBase_Object"');
		}
		if ($gettersClassesPrefix) {
			$this->gettersClassesPrefix = $gettersClassesPrefix;
		}
		if ($objectsClassesPrefix) {
			$this->objectsClassesPrefix = $objectsClassesPrefix;
		}
		if ($tablesPrefix) {
			$this->tablesPrefix = $tablesPrefix;
		}
	}

	public function setTablesPrefix($prefix) {
		$this->tablesPrefix = $prefix;
	}

	protected function isClassExists($class, $isSubclassOf = null) {
		try {
			$classExists = class_exists($class);
		}
		catch (Exception $e) {
			$classExists = false;
		}
		if ($classExists) {
			if ($isSubclassOf && $class != $isSubclassOf && !is_subclass_of($class, $isSubclassOf)) {
				if ($this->$this->throwExceptionOnWrongSubclass) {
					throw new DaBase_Exception('Class "' . $class . '" is not subclass of "' . $isSubclassOf . '"');
				}
			}
			else {
				return true;
			}
		}
		return false;
	}

	public function throwExceptionOnWrongSubclass($throw) {
		$this->throwExceptionOnWrongSubclass = $throw;
	}

	public function getGetterByName($getterName, DaBase_Db $db) {
		if ($getter = $this->getCachedGetter($getterName, $db)) {
			return $getter;
		}
		
		$getterClass = $this->getGetterClassByGetterName($getterName);
		$objectsClass = constant("$getterClass::objectsClass") ? constant("$getterClass::objectsClass") : $this->getObjectsClassByGetterName($getterName);
		
		if (constant("$objectsClass::table")) {
			$tableName = constant("$objectsClass::table");
		}
		elseif ($objectsClass != $this->defaultObjectsClass) {
			$tableName = $this->getTableNameByObjectsClass($objectsClass);
		}
		else {
			$tableName = $this->getTableNameByGetterName($getterName);
		}
		
		$getter = new $getterClass($db, $getterName, $tableName, $objectsClass);
		
		$this->setCachedGetter($getterName, $getter, $db);
		return $getter;
	}

	/**************************************************************
	 ROUTS CACHE
	 **************************************************************/
	
	protected function setCachedGetter($getterName, DaBase_Getter $getter, DaBase_Db $db) {
		$this->cachedGetter[self::getCacheKey($getterName, $db)] = clone $getter;
	}

	protected static function getCacheKey($getterName, DaBase_Db $db) {
		return $getterName . spl_object_hash($db);
	}

	protected function getCachedGetter($getterName, DaBase_Db $db) {
		$key = self::getCacheKey($getterName, $db);
		return isset($this->cachedGetters[$key]) ? clone $this->cachedGetters[$key] : false;
	}

	/**************************************************************
	 ROUTS METHODS (GETTER -> OBJECT -> TABLE)
	 **************************************************************/
	
	public function getGetterClassByGetterName($getterName) {
		$getterClass = $this->gettersClassesPrefix . self::getClearGetterClassByGetterName($getterName);
		if ($this->isClassExists($getterClass, 'DaBase_Getter')) {
			return $getterClass;
		}
		return $this->defaultGettersClass;
	}

	public function getObjectsClassByGetterName($getterName) {
		$objectsClass = $this->objectsClassesPrefix . self::getClearObjectsClassByGetterName($getterName);
		if ($this->isClassExists($objectsClass, 'DaBase_Object')) {
			return $objectsClass;
		}
		return $this->defaultObjectsClass;
	}

	public function getTableNameByGetterName($getterName) {
		return $this->tablesPrefix . self::getClearTableNameByGetterName($getterName);
	}

	public function getTableNameByObjectsClass($objectsClass) {
		if($objectsClass != $this->defaultObjectsClass) {
			$objectsClass = preg_replace('/^' . $this->objectsClassesPrefix . '/', '', $objectsClass); 
		}
		return $this->tablesPrefix . self::getClearTableNameByObjectsClass($objectsClass);
	}

	public function getJoinFieldName($parentGetterName, $childGetterName) {
		return preg_replace('/(e?s)([A-Z]|$)/', '\\2', $parentGetterName) . 'Id';
	}

	/**************************************************************
	 NAMES CONVENTION (GETTER -> OBJECT -> TABLE)
	 **************************************************************/
	
	protected static function getClearGetterClassByGetterName($getterName) {
		return ucfirst($getterName);
	}

	protected static function getClearObjectsClassByGetterName($getterName) {
		return ucfirst(preg_replace('/(e?s)([A-Z]|$)/', '\\2', $getterName));
	}

	protected static function getObjectClassByGetterClass($getterClass) {
		return preg_replace('/(e?s)([A-Z]|$)/', '\\2', $getterClass);
	}

	protected static function getObjectClassByTable($tableName) {
		return preg_replace('/(e?s)$/', '', preg_replace('/(e)?s_(.?)/e', 'strtoupper("\\2")', ucfirst($tableName)));
	}

	protected static function getClearGetterClassByTable($tableName) {
		return preg_replace('/_(.?)/e', 'strtoupper("\\1")', ucfirst($tableName));
	}

	protected static function getClearTableNameByGetterName($getterName) {
		return strtolower(preg_replace('/([A-Z])/', '_\\1', $getterName));
	}

	protected static function getClearTableNameByObjectsClass($objectsClass) {
		$tableName = strtolower(preg_replace('/.([A-Z])/', '_\\1', $objectsClass));
		$tableName = preg_replace('/(_|$)/', 's\\1', $tableName);
		$tableName = preg_replace('/ss(_|$)/', 'ses\\1', $tableName);
		return $tableName;
	}
}